package tech.yixiyun.framework.kuafu.bean;

import tech.yixiyun.framework.kuafu.log.LOGGER;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiFunction;

/**
 * Bean实例化模式
 */
public enum BeanMode {
    /**
     * 单例模式
     */
    SINGLETON((definition, injectNeed) -> {

        if (injectNeed == false) {
            return normallySingleton(definition);
        }
        return injectSingleton(definition);

    }),

    /**
     * 多例模式
     */
    MULTITON((definition, injectNeed) -> {
        try {
            //多例跟状态无关，直接标记为Over
            definition.setState(BeanState.OVER);
            Object t = definition.getFinalClass().newInstance();
            BeanContext.inject(t,  definition.getOriginalClass());
            return t;
        } catch (Exception e) {
            throw new BeanException("实例化" + definition.getOriginalClass() + "时发生异常", e);
        }
    });



    /**
     * 单例Bean在实例化时的锁
     */
    private static final ConcurrentHashMap<Class, ReentrantLock> beanLock = new ConcurrentHashMap<>();

    /**
     * 正常情况下获取单例实例
     * @param definition
     * @return
     */
    private static Object normallySingleton(BeanDefinition definition) {
        //单例模式，必须实例状态为Over才可对外提供
        if (definition.getState() == BeanState.OVER && definition.getInstance() != null) {
            return definition.getInstance();
        }

        ReentrantLock lock = beanLock.computeIfAbsent(definition.getOriginalClass(), k -> {
            return new ReentrantLock();
        });

        try {
            lock.lock();
            Object instance = createSinglonInstance(definition);
            //没标记完结的不允许对外提供引用
            long timer = System.currentTimeMillis();
            while (definition.setState(BeanState.OVER) == false) {
                if (System.currentTimeMillis() - timer >= 5000) {
                    //5s还没完成初始化，指定出问题了，报个异常把
                    throw new BeanException("5s内未完成" + definition.getOriginalClass() + "的实例化，请检查程序");
                }
                Thread.sleep(0, 100);
            }
            return instance;
        } catch (Exception e) {
            if (e instanceof RuntimeException) {
                throw (RuntimeException)e;
            }
            throw new BeanException("实例化" + definition.getOriginalClass() + "时发生异常", e);

        } finally {
            lock.unlock();
            beanLock.remove(definition.getOriginalClass());
        }
    }

    /**
     * 依赖注入时获取单例实例
     * @param definition
     * @return
     */
    private static Object injectSingleton(BeanDefinition definition) {
        //先检查是否实例化完了
        if (definition.getState().getValue() >= BeanState.INITING.getValue()
                && definition.getInstance() != null) {
            return definition.getInstance();
        }
        //没有的话，尝试获取锁
        ReentrantLock lock = beanLock.computeIfAbsent(definition.getOriginalClass(), k -> {
            return new ReentrantLock();
        });
        boolean success = false;
        try {

            success = lock.tryLock();
            //锁获取成功了，就可以去实例化了
            if (success) {
                return createSinglonInstance(definition);
            } else {
                //锁失败了，说明已经被其他线程抢先执行了，就尝试将它更新为合作态
                //如果更新失败，说明这个bean还没实例化好，需要等一等
                if (definition.setState(BeanState.COOPERATING) == false) {
                    Thread.sleep(1);
                    return injectSingleton(definition);
                }
                return definition.getInstance();
            }
        } catch (Exception e) {
            if (e instanceof RuntimeException) {
                throw (RuntimeException) e;
            }
            throw new BeanException("实例化" + definition.getOriginalClass() + "时发生异常", e);
        } finally {
            if (success) {
                lock.unlock();
            }
            beanLock.remove(definition.getOriginalClass());
        }
    }



    /**
     * 实例化方法
     * 一个是要实例化的BeanDefinition
     * 一个是否是依赖注入时需要的实例化，true代表是，false代表普通情况下需要的实例化。对于单例来说，这是两个逻辑
     * Object代表获取的实例结果
     */
    private BiFunction<BeanDefinition,Boolean, Object> instantiationFunction;

    private BeanMode(BiFunction<BeanDefinition,Boolean, Object> function) {
        this.instantiationFunction = function;
    }

    /**
     * 获取实例
     * @param definition
     * @param injectNeed 是否是依赖注入需要的实例
     * @return
     */
    protected  <T> T getInstance(BeanDefinition definition, Boolean injectNeed) {
        return (T) instantiationFunction.apply(definition, injectNeed);
    }

    /**
     * 创建单例实例
     * @param definition
     * @param <T>
     * @return
     */
    private static   <T> T createSinglonInstance(BeanDefinition definition) {
        Class<T> originalClass = definition.getOriginalClass();

        if (definition.getState() == BeanState.OVER) {
            return (T) definition.getInstance();
        }
        try {
            T t = (T) definition.getFinalClass().newInstance();
            definition.setInstance(t);
            definition.setState(BeanState.INITING);

            BeanContext.inject(t,  originalClass);

            definition.setState(BeanState.READY);


            return t;
        } catch (Exception e) {
            definition.setState(BeanState.NOT_INIT);
            throw new BeanException("实例化" + definition.getOriginalClass() + "时发生异常", e);
        }

    }

}
